package transclusion.items;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import transclusion.DAG;
import wordcram.Word;

public class Category implements Itemable {
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Category other = (Category) obj;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}	

	DAG dag;
	String name;
	Category parent;
	
	public Category(DAG d, String name) {
		this(d, name, null);
	}
	
	public Category(DAG d, String name, Category category) {
		this.dag = d;
		this.name = name;
		this.parent = category;
	}	
	
	public String name() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public Category parent() {		
		return parent;
	}
	
	public void setParent(Itemable p) {
		this.parent = (Category) p;
	}
	
	/**
	 * returns children categories
	 */
	public ArrayList<?> children() {
		return dag.categoryDAG.get(this);
	}	
	
	public ArrayList<Article> articleChildren() {
		return dag.categoryArticles.get(this);
	}
	
	public ArrayList<Itemable> list() {
		ArrayList<Itemable> result = null;
		ArrayList<Category> l1 = (ArrayList<Category>) children();
		ArrayList<Article> l2 = articleChildren();		
		
		if( (l1 != null ) && (l2 != null ) ) {
			result = new ArrayList<Itemable>(); 
			result.addAll(l1);
			result.addAll(l2);
		} else if( (l1 != null ) && (l2 == null ) ) {
			result = new ArrayList<Itemable>(); 
			result.addAll(l1);
		} if( (l1 == null ) && (l2 != null ) ) {
			result = new ArrayList<Itemable>();
			result.addAll(l2);
		}
		return result;
	}
	
	public ArrayList<Itemable> orderedList() {
		ArrayList<Itemable> result = list();
		if(result != null) {
			Collections.sort(result, new Comparator<Itemable>() {
				public int compare(Itemable o1, Itemable o2) {
					String s1 = o1.name();
					String s2 = o2.name();
					return s1.compareTo(s2);
				} 
			});			
		}
		return result;
	}
	
	public ArrayList<Word> words() {
		ArrayList<Word> result = new ArrayList<Word>();
		result.add(new Word(this.name(), 26));		
		ArrayList<Category> childrenList = (ArrayList<Category>) children();
		if (childrenList != null)
			for( Category category : childrenList )
				result.add(new Word(category.name(), 16));
		return result;
	}
	
	public Word[] wordsArray() {
		int size = 1;
		ArrayList<Category> childrenList = (ArrayList<Category>) children();
		if (childrenList != null)
			size = size + childrenList.size();
		Word[] result = new Word[size];		
		result[0] = new Word(this.name(), 26);
		for(int i = 1; i < size; i++) 
			result[i] = new Word(childrenList.get(i-1).name(), 16);		
		return result;
	}
	
	public long length() {
		int catChildren = 0;
		int artChildren = 0;
		if(dag.categoryDAG.get(this) != null)		
			catChildren = 100 * dag.categoryDAG.get(this).size();
		if(dag.categoryArticles.get(this) != null)
			artChildren = 10 * dag.categoryArticles.get(this).size();
		return catChildren + artChildren;
	}

	/**
	public long priority() {
		int children = 0;
		int grandChildren = 0;
		ArrayList<Article> articlesList;
		if(dag.categoryDAG.get(this) != null) {
			ArrayList<Category> childrenList = dag.categoryDAG.get(this);
			for( Category category : childrenList ) {
				articlesList = dag.parentCategories.get(category);
				if( articlesList != null )
					grandChildren += articlesList.size();
			}
		}
		articlesList = dag.parentCategories.get(this); 
		if(articlesList != null)
			children = articlesList.size();		
		return children + grandChildren;
	}
	*/
	
	public long priority() {
		long result = 0;
		
		ArrayList<Article> articleChildren = dag.categoryArticles.get(this);
		if (articleChildren != null) {			
			result += articleChildren.size();
			for( Article article : articleChildren )
				result += dag.transcludedArticles.get(article).size();
		}
		
		if(!isLeaf()) {
			ArrayList<Category> categoryChildren = dag.categoryDAG.get(this);
			for( Category category : categoryChildren )
				result += category.priority();
		}
		
		return result;
	}	
	
	public void categoryDescendants(ArrayList<IndexedItem> catDescendants) {
		if(catDescendants == null)
			throw new RuntimeException("passed parameter should be non null");
		catDescendants.clear();
		categoryDescendants(catDescendants, -1);
	}

	protected void categoryDescendants(ArrayList<IndexedItem> catDescendants, int catIndex) {		
		ArrayList<Category> catChildren = (ArrayList<Category>) children();		
		if( catChildren != null ) {
			for( Category cat : catChildren ) {
				catDescendants.add(new IndexedItem(cat, catIndex));
				cat.categoryDescendants(catDescendants, catDescendants.size()-1);
			}							
		}
	}
	
	public void articleDescendants(ArrayList<IndexedItem> catDescendants, ArrayList<IndexedItem> artDescendants) {
		if(catDescendants == null || artDescendants==null)
			throw new RuntimeException("passed parameters should be non null");
		catDescendants.clear();		
		artDescendants.clear();
		
		articleDescendants(catDescendants, -1, artDescendants);
	}

	protected void articleDescendants(ArrayList<IndexedItem> catDescendants, int catIndex, ArrayList<IndexedItem> artDescendants) {
		ArrayList<Article> artChildren = articleChildren();
		if(artChildren != null)
			for( Article art : artChildren )
				artDescendants.add(new IndexedItem(art, catIndex));		
		
		ArrayList<Category> catChildren = (ArrayList<Category>) children();		
		if( catChildren != null ) {
			for( Category cat : catChildren ) {
				catDescendants.add(new IndexedItem(cat, catIndex));
				cat.articleDescendants(catDescendants, catDescendants.size()-1, artDescendants);
			}			
		}
	}

	public boolean isArticle() {		
		return false;
	}

	public boolean isCategory() {
		return true;
	}
	
	public boolean isLeaf() {
		boolean result;
		if(dag.categoryDAG.get(this) == null)		
			result = true;		
		else
			result = dag.categoryDAG.get(this).isEmpty();
		
		//debug
		/**
		if(result)
			PApplet.println(this.name() +  " category is a leaf");
		else 
			PApplet.println(this.name() +  " category is not a leaf");
		// */
		
		return result;
	}
}
